/*-----------------------------------------------------------------------*/
/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2007        */
/*-----------------------------------------------------------------------*/
/* This is a stub disk I/O module that acts as front end of the existing */
/* disk I/O modules and attach it to FatFs module with common interface. */
/*-----------------------------------------------------------------------*/
#include <nxp/iolpc2378.h>
#include "diskio.h"
#include "mci.h"
#include "dma.h"
/* Scheduler includes. */
#include "FreeRTOS.h"
#include "queue.h"
#include "task.h"
//#include "type.h"
#include "irq.h"
#include <string.h> 
//#include "string.h"
extern volatile DWORD CardType;
int ALREADY_INIT=0;
extern volatile DWORD MCI_CardType;
extern volatile DWORD MCI_Block_End_Flag;
#include "board.h"
static volatile

/*-----------------------------------------------------------------------*/
/* Correspondence between drive number and physical drive                */
/* Note that Tiny-FatFs supports only single drive and always            */
/* accesses drive number 0.                                              */

#define ATA		0
#define MMC		1
#define USB		2
#ifdef __GNUC__

#define myprintf xprintf
#else
#include <stdio.h>
#define myprintf printf
#endif

#ifdef __ARMCC_VERSION
// RealView
#define INLINE __inline
#else
// gcc
#define INLINE inline
#endif

extern volatile DWORD MCI_CardType; 
extern volatile DWORD MCI_Block_End_Flag;


#define CURR_STATE_IDLE     0
#define CURR_STATE_READY    1
#define CURR_STATE_IDENT    2
#define CURR_STATE_STBY     3
#define CURR_STATE_TRAN     4
#define CURR_STATE_DATA     5
#define CURR_STATE_RCV      6
#define CURR_STATE_PRG      7
#define CURR_STATE_DIS      8


static volatile
DWORD Timer1, Timer2;	/* 1000Hz decrement timer */

static volatile
DSTATUS Stat = STA_NOINIT;	/* Disk status */

//static
//UINT CardType;			/* b0:MMC, b1:SDC, b2:Block addressing */

//static BYTE csd[16]; /* cached csd */

//static BYTE sd_status[16]; /* cached 16 MS-Bytes from SD_STATUS (ACMD13) */


#ifdef DISKIO_DUMPSTAT

const char* states[] = {
	"idle", "ready", "ident", "stby", 
	"tran", "data", "rcv", "prg", "dis"
};

static volatile void dump_status( const char* info )
{
	DWORD st;
	DWORD currst;

	st = MCI_Send_Status();
	if ( st == INVALID_RESPONSE ) {
		//diskio_printF("dump_status: Send_Status INVALID_RESPONSE\n");
		return;
	}

	//diskio_printF("------ Status register %s\n -----", info);
	//diskio_printF("AKE_SEQ_ERROR    : %s\n",  (st & (1 <<  3)) ? "error"     : "no error" );
	//diskio_printF("APP_CMD          : %s\n",  (st & (1 <<  5)) ? "enabled"   : "disabled" );
	//diskio_printF("READY_FOR_DATA   : %s\n",  (st & (1 <<  8)) ? "not ready" : "ready"    );

	currst = (st >> 9) & 0x0f;
	if ( currst > 8 ) {
		//diskio_printF("CURR_STATE       : reserved\n");
	}
	else {
		//diskio_printF("CURR_STATE       : %s   <---\n", states[currst]);
	}
	// TODO bits 13-18
	//diskio_printF("ERROR            : %s\n", (st & (1<<19)) ? "error"  : "no error" );
	//diskio_printF("CC_ERROR         : %s\n", (st & (1<<20)) ? "error"  : "no error" );
	//diskio_printF("CARD_ECC_FAILED  : %s\n", (st & (1<<21)) ? "failure": "success"  );
	//diskio_printF("ILLEGAL_COMMAND  : %s\n", (st & (1<<22)) ? "error"  : "no error" );
	//diskio_printF("COM_CRC_ERROR    : %s\n", (st & (1<<23)) ? "error"  : "no error" );
	//diskio_printF("LOCK_UNLOCK_FAIL : %s\n", (st & (1<<24)) ? "error"  : "no error" );
	//diskio_printF("CARD_IS_LOCKED   : %s\n", (st & (1<<25)) ? "locked" : "unlocked" );
	// TODO bits 26-28
	//diskio_printF("BLOCK_LEN_ERROR  : %s\n", (st & (1<<29)) ? "error"  : "no error" );
	//diskio_printF("ADDRESS_ERROR    : %s\n", (st & (1<<30)) ? "error"  : "no error" );
	//diskio_printF("OUT_OF_RANGE     : %s\n", (st & (1<<31)) ? "error"  : "no error" );
}
#else 
#define dump_status(s__)
#endif

static INLINE USHORT currstate_from_status( DWORD st )
{
	return ( (st >> 9 ) & 0xff );
}

static INLINE BYTE status_ready_bit( DWORD st )
{
	return ( st & ( 1<<8 ) ) ? 1 : 0;
}
#if MCI_DMA_ENABLED

extern volatile BYTE *WriteBlock, *ReadBlock;
volatile DWORD TXBlockCounter, RXBlockCounter;
#else
BYTE *src_addr;
BYTE *dest_addr;


#endif
// state to trans (SD Card State Diagram: V2.2 P4-7)
static void SELECT(void)
{
	DWORD currst, st;
	dump_status("before select");

	Timer2 = 1000;
	do {
		// cmd7 until state is trans
		st = MCI_Send_Status();
		currst = currstate_from_status(st);
		if ( currst != CURR_STATE_TRAN ) {
			if ( MCI_Select_Card() == FALSE ) {
				////diskio_printF("SELECT Select_Card failed - retry\n");
			}
		}
	} while ( ( currst != CURR_STATE_TRAN ) && Timer2 );

	if ( Timer2 == 0 ) {
		////diskio_printF("SELECT timeout changeing to trans\n");
	}
	dump_status("after select");
}

// state to stby
static void DESELECT(void)
{
	DWORD cardtype_save, st, currst;

	Timer2 = 500;
	do {
		st = MCI_Send_Status();
		
		currst = currstate_from_status(st);
	} while ( ( currst != CURR_STATE_TRAN ) && 
	          ( currst != CURR_STATE_STBY  ) && 
	          Timer2 );

	if ( Timer2 == 0 ) {
		//diskio_printF("DESELCT timeout waiting trans or stby\n");
	}
	else if ( currst == CURR_STATE_TRAN ) {
		cardtype_save = MCI_CardType;
		// CMD7: trans to stby (RCA 0) / de-select
		MCI_CardType = 0;
		if ( MCI_Select_Card() == FALSE ) {
			//diskio_printF("DESELECT failed\n");
		}
		MCI_CardType = cardtype_save;
	}
	else {
		////diskio_printF("DSELECT already in stby\n");
	}
	
	return;
}

// wait for stby or trans with timeout
static BYTE wait_ready( void ) 
{
	BYTE res;
	DWORD st;
	// DWORD currst;

	res = 0xaa;
	Timer2 = 500;	/* Wait for ready in timeout of 500ms */
	do {
		st = MCI_Send_Status();
		if ( st == INVALID_RESPONSE ) {
			//diskio_printF("wait_ready: Send_Status INVALID_RESPONSE\n");
		}
		else {
			// currst = currstate_from_status(st);
			// if ( currst == 3 /* stby */ || currst == 4 /* trans */) {
			if ( status_ready_bit( st ) ) {
				res = 0xff;
			}
		}
	}
	while ( (res != 0xFF) && Timer2 );

	if ( Timer2 == 0 ) {
		//diskio_printF("wait_ready: timeout stby or trans\n");
	}

	return res;
}

// well, no SPI used here but will keep the interface compatible with
// Chan's examples
static void release_spi(void)
{
	DESELECT();
}
/*-----------------------------------------------------------------------*/
/* Inidialize a Drive                                                    */
static void power_off (void)
{
	SELECT();
	wait_ready();
	release_spi();

	MCIPOWER = 0;

	Stat |= STA_NOINIT;		/* Set STA_NOINIT */
}

void sd_power_off(void)
{
	SELECT();
	wait_ready();
	release_spi();
	MCIPOWER = 0;
      PINMODE4 = 0x0A800000; //msi Dis    
        CardType=0;
        MCI_CardType=0;
        
        PCONP_bit.PCSDC = 0;        // DISABLE MCI clk
        PCONP_bit.PCGPDMA = 0;        // DISABLE GPDMA clk
        ///PCONP =0x43481298;/* DISABLE GPDMA clock */
     //  PCONP = ( 0 << 29 );     /* Enable clock to the MCI block */
        ALREADY_INIT=0;      
}





DSTATUS disk_initialize (
	BYTE drv				/* Physical drive nmuber (0..) */
)
{
  
	if ( drv != 0 ) return STA_NOINIT;

  //  portENTER_CRITICAL(); 
        Stat &= ~STA_NOINIT;
        if (ALREADY_INIT==0)
        {
	if ( MCI_disk_initialize() ) 
        {
		// error occured 
		Stat |= STA_NOINIT;
		power_off();
	}
        ALREADY_INIT=1;
        }
        
  //  portEXIT_CRITICAL();
	return Stat;
        
        
        
}

static int MCI_disk_initialize(void)
{
  
	DWORD i;//, j;
	//DWORD csddw[4];
  /* Init Card present and Write protect switches */
  SCS_bit.GPIOM = 1;          /* enable fast io for GPIO0,1 */
//MMC_CP_DIR &= ~MMC_CP_MASK; /* Card present port - input */
//MMC_WP_DIR &= ~MMC_WP_MASK; /* Write protect port - input */
#if MCI_DMA_ENABLED
  /* on DMA channel 0, source is memory, destination is MCI FIFO. */
  /* On DMA channel 1, source is MCI FIFO, destination is memory. */
  PCONP |= (1 << 29); /* Enable GPDMA clock */

 // src_addr = (BYTE *)DMA_SRC;
  //dest_addr = (BYTE *)DMA_DST;
  for ( i = 0; i < DMA_SIZE; i++ )
  {
   // *src_addr = i;
    //*dest_addr = 0;
    //src_addr++;
    //dest_addr++;
  }
#else
  for ( i = 0; i < BLOCK_LENGTH; i++ )
  {
    WriteBlock[i] = i;
    ReadBlock[i] = 0;
  }
#endif

  /* For the SD card I tested, the minimum required block length is 512 */
  /* For MMC, the restriction is loose, due to the variety of SD and MMC
  card support, ideally, the driver should read CSD register to find the
  right speed and block length for the card, and set them accordingly.
  In this driver example, it will support both MMC and SD cards, and it
  does read the information by send SEND_CSD to poll the card status,
  however, to simplify the example, it doesn't configure them accordingly
  based on the CSD register value. This is not intended to support all
  the SD and MMC cards. */

  if ( MCI_Init() != TRUE )
  {
   // while( 1 );     /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( (CardType = MCI_CardInit()) == CARD_UNKNOWN )
  {
    //while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( MCI_Check_CID() != TRUE )
  {
   // while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( MCI_Set_Address() != TRUE )
  {
    //while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( MCI_Send_CSD() != TRUE )
  {
   // while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( MCI_Select_Card() != TRUE )
  {
  //  while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }

  if ( CardType == SD_CARD )
  {
    MCICLOCK_bit.WIDEBUS = 1;   /* Use wide bus for SD */
    for ( i = 0; i < 0x20; i++ );
    
      Set_MCIClock(NORMAL_RATE);
    //..MCI_Set_MCIClock( NORMAL_RATE );
    
    if ( MCI_Send_ACMD_Bus_Width( BUS_WIDTH_4BITS ) == FALSE )
    {
    //  while ( 1 );  /* fatal error */
      		Stat |= STA_NOINIT;
                	return Stat;
    }
  }

  if ( MCI_Set_BlockLen( BLOCK_LENGTH ) != TRUE )
  {
  //  while ( 1 );    /* fatal error */
    		Stat |= STA_NOINIT;
                	return Stat;
  }
//
/*
  for ( i = 0; i < BLOCK_NUM; i++ )
  {
#if !MCI_DMA_ENABLED
    TXBlockCounter = 0;
    RXBlockCounter = 0;
    src_addr = (BYTE *)DMA_SRC;
    dest_addr = (BYTE *)DMA_DST;
#endif
  }
  */
		Stat &= ~STA_NOINIT;
  
  	return Stat;

}
/*-----------------------------------------------------------------------*/
/* Return Disk Status                                                    */

DSTATUS disk_status (
	BYTE drv		/* Physical drive nmuber (0..) */
)
{
	if ( drv != 0 ) return STA_NOINIT;

	return Stat;
  
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */

DRESULT disk_read (
	BYTE drv,		/* Physical drive nmuber (0..) */
	BYTE *buff,		/* Data buffer to store read data */
	DWORD sector,	/* Sector number (LBA) */
	BYTE count		/* Sector count (1..255) */
)
{
	// DRESULT res;
	//WORD k;

	if (drv || !count) return RES_PARERR;
	if (Stat & STA_NOINIT) return RES_NOTRDY;

	// done in MCI driver if (!(CardType & 4)) sector *= 512;	/* Convert to byte address if needed */

	/* Select the card and wait for ready */
	/* mthomas - not sure if this is needed for SD */
	DESELECT();
	//if (wait_ready() != 0xFF) return (DRESULT)(0xFF);
	SELECT();

	// TODO: Multi-block
	do {
		//diskio_printfr("Read_Block start...\n");
		if ( MCI_Read_Block( sector ) != TRUE ) {
			//diskio_printf("MCI_Read_Block failed\n");
			break;
		}
		else {
			
			//diskio_printfr("Read_Block wait...\n");
			/* When MCI_Block_End_Flag is clear, it indicates RX is done */
			while ( MCI_Block_End_Flag == 1 ) { ; }
			//diskio_printfr("Read_Block end wait\n");
			// laebi: memcpy is faster than the for-Loop
			// TODO: avoid copy
			memcpy(buff,(BYTE*)ReadBlock, 512);
			// mziegert 20090823: Bugfix, memcpy does not increase buff pointer
			buff = &buff[512];
			sector++;
		}
	} while ( --count );
	
	release_spi();

	return count ? RES_ERROR : RES_OK;
}



/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */

#if _READONLY == 0
DRESULT disk_write (
	BYTE drv,			/* Physical drive nmuber (0..) */
	const BYTE *buff,	/* Data to be written */
	DWORD sector,		/* Sector number (LBA) */
	BYTE count			/* Sector count (1..255) */
)
{
	//DRESULT res;
	//int result;
  	if (drv || !count)
		return RES_PARERR;
	if (Stat & STA_NOINIT)
		return RES_NOTRDY;
	if (Stat & STA_PROTECT)
		return RES_WRPRT;

	// done in mci-driver: if (!(CardType & 4)) sector *= 512;	/* Convert to byte address if needed */

	/* Select the card and wait for ready */
	/* mthomas - not sure if this is needed for SD */
	DESELECT();
	//if (wait_ready() != 0xFF) return (DRESULT)0xFF;
	SELECT();

	//do {
	//diskio_printfw("Write_Block %d start...\n", sector );
	if (count > 1) {
		// Multi Block Write
		if (MCI_Write_Multiple_Block(sector, count, buff) != TRUE) {
			//diskio_printf("MCI_Write_Multiple_Block failed\n");
			return RES_ERROR;
			//break;
		} else {
			//diskio_printfw("Write_Block Multi wait...\n");
			/* When MCI_Block_End_Flag is clear, it indicates TX is done */
			while (MCI_Block_End_Flag == 1) {
				;
			}
			//diskio_printfw("Write_Block Multi end wait\n");
		}
	} else {
		// Single Block write
		// TODO: avoid copy
		memcpy((BYTE*)WriteBlock, buff, 512);
		if (MCI_Write_Block(sector) != TRUE) {
			//diskio_printf("MCI_Write_Block failed\n");
			return RES_ERROR;
			//break;
		} else {
			//diskio_printfw("Write_Block wait...\n");
			/* When MCI_Block_End_Flag is clear, it indicates TX is done */
			while (MCI_Block_End_Flag == 1) {
				;
			}
			//diskio_printfw("Write_Block end wait\n");
		}
	}

	// laebi: not necessary
	//release_spi();

	//return count ? RES_ERROR : RES_OK;
	return RES_OK;
  


}
#endif /* _READONLY */



/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */

DRESULT disk_ioctl (
	BYTE drv,		/* Physical drive nmuber (0..) */
	BYTE ctrl,		/* Control code */
	void *buff		/* Buffer to send/receive control data */
)
{
	DRESULT res;
	int result;


}




void disk_timerproc (void)
{
	DWORD n;

	n = Timer1;						/* 1000Hz decrement timer */
	if (n) Timer1 = --n;
	n = Timer2;
	if (n) Timer2 = --n;

#if 0
	static BYTE pv;
	BYTE s, p;

	p = pv;
	pv = SOCKPORT & (SOCKWP | SOCKINS);	/* Sample socket switch */

	if (p == pv) {					/* Have contacts stabled? */
		s = Stat;

		if (p & SOCKWP)				/* WP is H (write protected) */
			s |= STA_PROTECT;
		else						/* WP is L (write enabled) */
			s &= ~STA_PROTECT;

		if (p & SOCKINS)			/* INS = H (Socket empty) */
			s |= (STA_NODISK | STA_NOINIT);
		else						/* INS = L (Card inserted) */
			s &= ~STA_NODISK;

		Stat = s;
	}
#endif
}
